package org.amos.server.modules.upms.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.amos.core.basic.annotation.Log;
import org.amos.core.basic.constant.SystemConstant;
import org.amos.core.basic.exception.ServiceException;
import org.amos.core.basic.utils.AmosUtils;
import org.amos.core.basic.utils.crud.WrapperBuilder;
import org.amos.core.frame.utils.IdUtils;
import org.amos.core.frame.utils.RedisUtils;
import org.amos.satoken.utils.PwdUtils;
import org.amos.server.constant.SysCacheConstant;
import org.amos.server.modules.upms.dto.*;
import org.amos.server.modules.upms.entity.Tenant;
import org.amos.server.modules.upms.entity.User;
import org.amos.server.modules.upms.entity.UserDept;
import org.amos.server.modules.upms.mapper.UserDeptMapper;
import org.amos.server.modules.upms.mapper.UserMapper;
import org.amos.server.modules.upms.mapper.UserPostMapper;
import org.amos.server.modules.upms.mapper.UserRoleMapper;
import org.amos.server.modules.upms.service.*;
import org.amos.server.modules.upms.vo.UserVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 用户服务实现类
 *
 * @author CodeGenerator
 * @since 2020-12-20
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Autowired
    @Lazy
    private IUserRoleService userRoleService;
    private final IUserPostService userPostService;
    private final IUserDeptService userDeptService;
    private final UserRoleMapper userRoleMapper;
    private final UserDeptMapper userDeptMapper;
    private final UserPostMapper userPostMapper;
    private final IDeptService deptService;
    @Autowired
    @Lazy
    private ITenantService tenantService;
    private final RedisUtils redisUtils;

    @Override
    public User getUserByMobile(String mobile) {
        Assert.state(StrUtil.isAllNotBlank(mobile), "mobile is blank");
        QueryWrapper<User> qw = new QueryWrapper<>();
        qw.eq(AmosUtils.toDbField(User::getPhone), mobile)
                .eq(AmosUtils.toDbField(User::getIsDeleted), SystemConstant.SYS_DELETE_FLAG_DEFAULT);
        return  baseMapper.selectOne(qw);
    }

    @Override
    public User getUserByAccount(String account) {
        Assert.state(StrUtil.isAllNotBlank(account), "account is blank");
        return baseMapper.getUserByAccount(account);
    }

    @Override
    public User findByName(String username) {
        QueryWrapper<User> qw = new QueryWrapper<>();
        qw.eq(AmosUtils.toDbField(User::getUserName), username)
                .eq(AmosUtils.toDbField(User::getIsDeleted), SystemConstant.SYS_DELETE_FLAG_DEFAULT);
        User user = baseMapper.selectOne(qw);
        return user;
    }

    @Override
    public User findById(Long id) {
        return super.getById(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @Log(value = "[修改用户]")
    public Long saveOrUpdateUser(UserDTO dto) {
        Tenant tenant = tenantService.getTenant();
        if (Objects.nonNull(tenant)) {
            QueryWrapper<User> qw = new QueryWrapper<>();
            qw.eq(AmosUtils.toDbField(User::getIsDeleted), SystemConstant.SYS_DELETE_FLAG_DEFAULT);
            long count = super.count(qw);
            if ((count > tenant.getAccountCount()) || (count == tenant.getAccountCount() && Objects.isNull(dto.getId()))) {
                throw new ServiceException("租户账号数量已超限制!");
            }
        }
        User user = AmosUtils.copy(dto, User.class);
        Set<Long> roleIds = dto.getRoleIds();
        Set<Long> postIds = dto.getPostIds();
        Set<Long> deptIds = dto.getDeptIds();
        Long userId = user.getId();
        String password = PwdUtils.encode(SystemConstant.SYS_DEFAULT_PWD);
        if (StrUtil.isNotBlank(dto.getPassword())) {
            password = PwdUtils.encode(dto.getPassword());
        }
        if (Objects.isNull(userId)) {
            userId = IdUtils.nextId();
            user.setId(userId);
            user.setPassword(password);
        }
        super.saveOrUpdate(user);
        // 更新用户角色关联
        if (CollUtil.isNotEmpty(roleIds)) {
            userRoleService.updateUserRole(userId, roleIds);
        }
        // 更新用户岗位关联
        if (CollUtil.isNotEmpty(postIds)) {
            userPostService.updateUserPost(userId, postIds);
        }
        // 更新用户部门关联
        if (CollUtil.isNotEmpty(deptIds)) {
            userDeptService.updateUserDept(userId, deptIds);
        }
        return userId;
    }

    @Override
    @Log(value = "[重置用户密码]")
    public Boolean resetPwd(Set<Long> ids) {
        UpdateWrapper<User> uw = new UpdateWrapper<>();
        uw.in(AmosUtils.toDbField(User::getId), ids).set(AmosUtils.toDbField(User::getPassword), PwdUtils.encode(SystemConstant.SYS_DEFAULT_PWD));
        super.update(uw);
        return Boolean.TRUE;
    }

    @Override
    @Log(value = "[设置用户状态]")
    public Boolean setStatus(UserStatusDTO dto) {
        UpdateWrapper<User> uw = new UpdateWrapper<>();
        uw.in(AmosUtils.toDbField(User::getId), dto.getIds()).set(AmosUtils.toDbField(User::getStatus), dto.getStatus());
        boolean res = super.update(uw);
        if (res) {
            // 删除用户缓存, 性能考虑没有用模糊删除
            List<User> list = baseMapper.selectList(new WrapperBuilder().build(dto));
            List<String> accountKeyList = new ArrayList<>();
            List<String> userNameKeyList = list.stream().map(User::getUserName).
                    map((x -> SysCacheConstant.SYS_USER_ACCOUNT_KEY.concat(":").concat(x))).collect(Collectors.toList());
            List<String> emailKeyList = list.stream().map(User::getEmail).
                    map((x -> SysCacheConstant.SYS_USER_ACCOUNT_KEY.concat(":").concat(x))).collect(Collectors.toList());
            List<String> phoneKeyList = list.stream().map(User::getPhone).
                    map((x -> SysCacheConstant.SYS_USER_ACCOUNT_KEY.concat(":").concat(x))).collect(Collectors.toList());
            accountKeyList.addAll(userNameKeyList);
            accountKeyList.addAll(emailKeyList);
            accountKeyList.addAll(phoneKeyList);
            redisUtils.del(accountKeyList.toArray(new String[]{}));
        }

        return Boolean.TRUE;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @Log(value = "[删除用户]")
    public Boolean removeUser(Set<Long> ids) {
        // 系统默认ID 数据不能删除
        boolean contains = CollUtil.contains(ids, SystemConstant.SYS_DEFAULT_ID);
        if (contains) {
            throw new ServiceException("系统内置数据不能变更！");
        }

        UpdateWrapper<User> uw = new UpdateWrapper<>();
        uw.eq(AmosUtils.toDbField(User::getIsDeleted), SystemConstant.SYS_DELETE_FLAG_DEFAULT)
                .in(AmosUtils.toDbField(User::getId), ids)
                .set(AmosUtils.toDbField(User::getIsDeleted), SystemConstant.SYS_DELETE_FLAG_ALREADY);
        super.update(uw);
        userRoleService.removeByUserIds(ids);
        userPostService.removeByUserIds(ids);
        userDeptService.removeByUserIds(ids);
        return Boolean.TRUE;
    }

    @Override
    public IPage<UserVO> selectUserPage(Page<User> page, UserDTO dto) {
        QueryWrapper<User> qw = new WrapperBuilder().build(dto);

        Long deptId = dto.getDeptId();
        if (Objects.nonNull(deptId)) {
            List<Long> children = deptService.getChildrenByParenId(deptId);
            QueryWrapper<UserDept> userDeptQueryWrapper = new QueryWrapper<>();
            userDeptQueryWrapper.in(AmosUtils.toDbField(UserDept::getDeptId), children);
            List<UserDept> userDepts = userDeptService.list(userDeptQueryWrapper);
            if (CollUtil.isNotEmpty(userDepts)) {
                List<Long> userIds = userDepts.stream().map(UserDept::getUserId).collect(Collectors.toList());
                qw.in(AmosUtils.toDbField(User::getId), userIds);
            }else {
                qw.in(AmosUtils.toDbField(User::getId), -1);
            }
        }
        IPage<User> userPage =super.page(page, qw);
        IPage<UserVO> userVOPage = new Page<>();
        if (userPage.getTotal() > 0) {
            Set<Long> userIds = userPage.getRecords().stream().map(User::getId).collect(Collectors.toSet());
            List<UserDeptDTO> depts = userDeptMapper.selectDepts(userIds);
            List<UserRoleDTO> roles = userRoleMapper.selectRoles(userIds);
            List<UserPostDTO> posts = userPostMapper.selectPosts(userIds);

            userVOPage = AmosUtils.pageCopy(userPage, UserVO.class);
            userVOPage.getRecords().forEach(x -> {
                String deptName = depts.stream().filter(d -> d.getUserId().equals(x.getId())).map(UserDeptDTO::getDeptName).collect(Collectors.joining(StrUtil.COMMA));
                Set<Long> deptIds = depts.stream().filter(d -> d.getUserId().equals(x.getId())).map(UserDeptDTO::getDeptId).collect(Collectors.toSet());
                String roleName = roles.stream().filter(d -> d.getUserId().equals(x.getId())).map(UserRoleDTO::getRoleName).collect(Collectors.joining(StrUtil.COMMA));
                Set<Long> roleIds = roles.stream().filter(d -> d.getUserId().equals(x.getId())).map(UserRoleDTO::getRoleId).collect(Collectors.toSet());
                String postName = posts.stream().filter(d -> d.getUserId().equals(x.getId())).map(UserPostDTO::getPostName).collect(Collectors.joining(StrUtil.COMMA));
                Set<Long> postIds = posts.stream().filter(d -> d.getUserId().equals(x.getId())).map(UserPostDTO::getPostId).collect(Collectors.toSet());
                x.setRoleName(roleName);
                x.setDeptName(deptName);
                x.setPostName(postName);
                x.setRoleIds(roleIds);
                x.setDeptIds(deptIds);
                x.setPostIds(postIds);
            });
        }

        return userVOPage;
    }
}
